package controllers

import (
	"fmt"
	"net/http"

	"github.com/saint-yellow/go-pl/toys/goblog/app/models/article"
	"github.com/saint-yellow/go-pl/toys/goblog/app/policies"
	"github.com/saint-yellow/go-pl/toys/goblog/app/requests"
	"github.com/saint-yellow/go-pl/toys/goblog/pkg/auth"
	"github.com/saint-yellow/go-pl/toys/goblog/pkg/logger"
	"github.com/saint-yellow/go-pl/toys/goblog/pkg/route"
	"github.com/saint-yellow/go-pl/toys/goblog/pkg/view"
	"gorm.io/gorm"
)

// ArticlesController 文章相关页面
type ArticlesController struct {
    BaseController
}

// Show 文章详情页面
func (*ArticlesController) Show(w http.ResponseWriter, r *http.Request) {
	id := route.GetRouteVariable("id", r)

    _article, err := article.Get(id)

    if err != nil {
        if err == gorm.ErrRecordNotFound {
            w.WriteHeader(http.StatusNotFound)
            fmt.Fprint(w, "404 文章未找到")
        } else {
            logger.LogError(err)
            w.WriteHeader(http.StatusInternalServerError)
            fmt.Fprint(w, "500 服务器内部错误")
        }
    } else {
        // 读取成功，显示文章
        view.Render(w, view.D{
            "Article": _article, 
            "CanModifyArticle": policies.CanModifyArticle(_article),
        }, "articles.show", "articles._article_meta")
}}

// Index 文章列表页
func (ac *ArticlesController) Index(w http.ResponseWriter, r *http.Request) {
    // 1. 获取结果集
    articles, pagerData, err := article.GetAll(r, 2)

    if err != nil {
        ac.ResponseForSQLError(w, err)
    } else {
        // 2. 加载模板
        view.Render(w, view.D{
            "Articles": articles,
            "PagerData": pagerData,
        }, "articles.index", "articles._article_meta")
    }
}

// Create 文章创建页面
func (*ArticlesController) Create(w http.ResponseWriter, r *http.Request) {
    view.Render(w, view.D{}, "articles.create", "articles._form_field")
}

// Store 文章创建页面 
func (*ArticlesController)  Store(w http.ResponseWriter, r *http.Request)  {
    // 1. 初始化数据
    currentUser := auth.User()
    _article := article.Article{
        Title:  r.PostFormValue("title"),
        Body:   r.PostFormValue("body"),
        UserID: currentUser.ID,
        CategoryID: 4,
    }

    // 2. 表单验证
    errors := requests.ValidateArticleForm(_article)

    // 3. 检测错误
    if len(errors) == 0 {
        // 创建文章
        _article.Create()
        if _article.ID > 0 {
            indexURL := route.NameToURL("articles.show", "id", _article.GetStringID())
            http.Redirect(w, r, indexURL, http.StatusFound)
        } else {
            w.WriteHeader(http.StatusInternalServerError)
            fmt.Fprint(w, "创建文章失败，请联系管理员")
        }
    } else {
        view.Render(w, view.D{
            "Article": _article,
            "Errors":  errors,
        }, "articles.create", "articles._form_field")
    }
}

// Edit 文章更新页面
func (ac *ArticlesController) Edit(w http.ResponseWriter, r *http.Request) {

    // 1. 获取 URL 参数
    id := route.GetRouteVariable("id", r)

    // 2. 读取对应的文章数据
    _article, err := article.Get(id)

    // 3. 如果出现错误
    if err != nil {
        ac.ResponseForSQLError(w, err)
    } else {
        // 检查权限
        if !policies.CanModifyArticle(_article) {
            ac.ResponseForUnauthorized(w, r)
        } else {
            // 4. 读取成功，显示编辑文章表单
            view.Render(w, view.D{
                "Article": _article,
                "Errors":  view.D{},
            }, "articles.edit", "articles._form_field")
        }
    }
}

// Update 更新文章
func (ac *ArticlesController) Update(w http.ResponseWriter, r *http.Request) {

    // 1. 获取 URL 参数
    id := route.GetRouteVariable("id", r)

    // 2. 读取对应的文章数据
    _article, err := article.Get(id)

    // 3. 如果出现错误
    if err != nil {
        ac.ResponseForSQLError(w, err)
    } else {
        // 4. 未出现错误

        // 检查权限
        if !policies.CanModifyArticle(_article) {
            ac.ResponseForUnauthorized(w, r)
        } else {
            // 4.1 表单验证
            _article.Title = r.PostFormValue("title")
            _article.Body = r.PostFormValue("body")

            errors := requests.ValidateArticleForm(_article)

            if len(errors) == 0 {

                // 4.2 表单验证通过，更新数据
                rowsAffected, err := _article.Update()

                if err != nil {
                    // 数据库错误
                    w.WriteHeader(http.StatusInternalServerError)
                    fmt.Fprint(w, "500 服务器内部错误")
                    return
                }

                // √ 更新成功，跳转到文章详情页
                if rowsAffected > 0 {
                    showURL := route.NameToURL("articles.show", "id", id)
                    http.Redirect(w, r, showURL, http.StatusFound)
                } else {
                    fmt.Fprint(w, "您没有做任何更改！")
                }
            } else {
                // 4.3 表单验证不通过，显示理由
                view.Render(w, view.D{
                    "Article": _article,
                    "Errors":  errors,
                }, "articles.edit", "articles._form_field")
            }
        }
    }
}

// Delete 删除文章
func (ac *ArticlesController) Delete(w http.ResponseWriter, r *http.Request) {

    // 1. 获取 URL 参数
    id := route.GetRouteVariable("id", r)

    // 2. 读取对应的文章数据
    _article, err := article.Get(id)

    // 3. 如果出现错误
    if err != nil {
        ac.ResponseForSQLError(w, err)
    } else {
        // 检查权限
        if !policies.CanModifyArticle(_article) {
            ac.ResponseForUnauthorized(w, r)
        } else {
            // 4. 未出现错误，执行删除操作
            rowsAffected, err := _article.Delete()

            // 4.1 发生错误
            if err != nil {
                // 应该是 SQL 报错了
                w.WriteHeader(http.StatusInternalServerError)
                fmt.Fprint(w, "500 服务器内部错误")
            } else {
                // 4.2 未发生错误
                if rowsAffected > 0 {
                    // 重定向到文章列表页
                    indexURL := route.NameToURL("articles.index")
                    http.Redirect(w, r, indexURL, http.StatusFound)
                } else {
                    // Edge case
                    w.WriteHeader(http.StatusNotFound)
                    fmt.Fprint(w, "404 文章未找到")
                }
            }
        }
    }
}

// todo: 请在创建文章时候，让用户可以选择分类。文章创建后，文章详情页里的 Meta 数据里，可以显示分类信息
